﻿using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Text;
using WeChatSDK.Common;
using WeChatSDK.Common.Enums.Http;
using WeChatSDK.Common.Enums.Material;
using WeChatSDK.RequestComponent.Http;
using WeChatSDK.WeChatDto.ErrorMessage;
using WeChatSDK.WeChatDto.Material;
using WeChatSDK.WeChatDto.Request.Http;
using WeChatSDK.WeChatServiceComponent.Conversation.BaseSupport;

namespace WeChatSDK.WeChatServiceComponent.Material
{
    /// <summary>
    /// 微信素材管理服务
    /// </summary>
    public class MaterialService
    {
        private readonly HttpHandle httpHandle;

        public MaterialService()
        {
            this.httpHandle = new HttpHandle();
        }
        #region 微信素材上传
        /// <summary>
        /// 检查内容长度
        /// </summary>
        /// <param name="materialDto">素材信息</param>
        /// <param name="permanentMaterial">是否为永久素材</param>
        /// <exception cref="WeChatException"></exception>
        /// <returns></returns>
        public void CheckContentLength(MaterialDto materialDto, bool permanentMaterial)
        {
            int maxSize = 1024 * 64;
            string typeTitle = "缩略图";
            string unit = "K";
            int number = 64;
            switch (materialDto.FileType)
            {
                case "image":
                    maxSize = 1024 * 1024 * 2;
                    number = 2;
                    typeTitle = "图片";
                    unit = "M";
                    break;
                case "voice":
                    maxSize = 1024 * 1024 * 2;
                    number = 2;
                    typeTitle = "音频";
                    unit = "M";
                    break;
                case "video":
                    maxSize = 1024 * 1024 * 10;
                    number = 10;
                    typeTitle = "视频";
                    unit = "M";
                    break;
                default:
                    break;
            }
            if (materialDto.FileLength > maxSize)
            {
                throw new WeChatException(string.Format("微信素材只允许上传 {0} 素材，且大小必须小于 {1}{2}。", typeTitle, number, unit));
            }
            if (permanentMaterial)
            {
                switch (materialDto.FileType)
                {
                    case "image":
                        if (!("bmp/png/jpeg/jpg/gif").Contains(materialDto.FileSuffix.Replace(".", "").ToLower()))
                        {
                            throw new WeChatException("微信素材永久上传图片只支持bmp/png/jpeg/jpg/gif格式，且大小必须小于2M");
                        }
                        break;
                    case "voice":
                        if (!("mp3/wma/wav/amr").Contains(materialDto.FileSuffix.Replace(".", "").ToLower()))
                        {
                            throw new WeChatException("微信素材永久上传声音只支持mp3/wma/wav/amr格式，且大小必须小于2M");
                        }
                        break;
                    case "video":
                        if (!("mp4").Contains(materialDto.FileSuffix.Replace(".", "").ToLower()))
                        {
                            throw new WeChatException("微信素材永久上传声音只支持MP4格式，且大小必须小于10MB。");
                        }
                        break;
                    default:
                        if (!("jpeg/jpg").Contains(materialDto.FileSuffix.Replace(".", "").ToLower()))
                        {
                            throw new WeChatException("微信素材永久上传缩略图只支持JPG格式，且大小必须小于64KB。");
                        }
                        break;
                }
            }
            else
            {
                switch (materialDto.FileType)
                {
                    case "image":
                        if (!("png/jpeg/jpg/gif").Contains(materialDto.FileSuffix.Replace(".", "").ToLower()))
                        {
                            throw new WeChatException("微信素材临时上传图片只支持png/jpeg/jpg/gif格式，且大小必须小于2M");
                        }
                        break;
                    case "voice":
                        if (!("mp3/amr").Contains(materialDto.FileSuffix.Replace(".", "").ToLower()))
                        {
                            throw new WeChatException("微信素材永久上传声音只支持mp3/amr格式，且大小必须小于2M");
                        }
                        break;
                    case "video":
                        if (!("mp4").Contains(materialDto.FileSuffix.Replace(".", "").ToLower()))
                        {
                            throw new WeChatException("微信素材永久上传声音只支持MP4格式，且大小必须小于10MB。");
                        }
                        break;
                    default:
                        if (!("jpeg/jpg").Contains(materialDto.FileSuffix.Replace(".", "").ToLower()))
                        {
                            throw new WeChatException("微信素材永久上传缩略图只支持JPG格式，且大小必须小于64KB。");
                        }
                        break;
                }
            }
        }

        /// <summary>
        /// 上传素材
        /// </summary>
        /// <param name="materialDto">素材文件信息</param>
        /// <param name="permanentMaterial">是否为永久素材</param>
        /// <exception cref="WeChatException"></exception>
        /// <returns>{"media_id":MEDIA_ID,"url":URL}</returns>
        public HttpResponseDto UploadMaterial(MaterialDto materialDto, bool permanentMaterial)
        {
            string contentType = string.Empty;
            string suffix = materialDto.FileSuffix.Replace(".", "");
            Dictionary<string, string> dic = new Dictionary<string, string>();
            dic.Add("bmp", "application/x-bmp");
            dic.Add("png", "image/png");
            dic.Add("jpeg", "image/jpeg");
            dic.Add("jpg", "image/jpeg");
            dic.Add("gif", "image/gif");
            dic.Add("mp3", "audio/mp3");
            dic.Add("wma", "audio/x-ms-wma");
            dic.Add("wav", "audio/wav");
            dic.Add("amr", "audio/amr");
            dic.Add("mp4", "video/mpeg4");
            if (permanentMaterial)
            {
                return UploadPermanentMaterial(materialDto, dic[suffix]);
            }
            else
            {
                return UploadTemporaryMaterial(materialDto, dic[suffix]);
            }
        }
        /// <summary>
        /// 上传永久素材
        /// </summary>
        /// <param name="materialDto">素材文件信息</param>
        /// <param name="contentType">内容类型</param>
        /// <returns></returns>
        private HttpResponseDto UploadPermanentMaterial(MaterialDto materialDto, string contentType)
        {
            string url = string.Format("https://api.weixin.qq.com/cgi-bin/material/add_material?access_token={0}&type={1}", new BaseSupportService().CreateAccessToken().AccessToken, materialDto.FileType);
            if (materialDto.FileType == "video")
            {
                return UploadVideoMaterial(url, materialDto, contentType);
            }
            HttpRequestDto httpRequestDto = new HttpRequestDto();
            httpRequestDto.Url = url;
            httpRequestDto.Headers.Add("Content-Type", contentType);
            httpRequestDto.Headers.Add("filename", materialDto.FileName+materialDto.FileSuffix);
            httpRequestDto.Headers.Add("filelength", materialDto.FileLength.ToString());
            httpRequestDto.Content = Encoding.UTF8.GetBytes(materialDto.FullFileName);
            httpRequestDto.Method = MethodType.POST;
            httpRequestDto.ContentType = FormatType.FormData;
            return httpHandle.HttpSendRequest(httpRequestDto);  //微信永久素材必须使用的是HttpClient上传
        }
        /// <summary>
        /// 上传临时素材
        /// </summary>
        /// <param name="materialDto">素材文件信息</param>
        /// <param name="contentType">内容类型</param>
        /// <returns></returns>
        private HttpResponseDto UploadTemporaryMaterial(MaterialDto materialDto, string contentType)
        {
            string url = string.Format("https://api.weixin.qq.com/cgi-bin/media/upload?access_token={0}&type={1}", new BaseSupportService().CreateAccessToken().AccessToken, materialDto.FileType);
            HttpRequestDto httpRequestDto = new HttpRequestDto();
            httpRequestDto.Url = url;
            httpRequestDto.Headers.Add("Content-Type", contentType);
            httpRequestDto.Headers.Add("filename", materialDto.FileName + materialDto.FileSuffix);
            httpRequestDto.Headers.Add("filelength", materialDto.FileLength.ToString());
            httpRequestDto.Content = Encoding.UTF8.GetBytes(materialDto.FullFileName);
            httpRequestDto.Method = MethodType.POST;
            httpRequestDto.ContentType = FormatType.FormData;
            return httpHandle.WebSendRequest(httpRequestDto);//微信临时素材必须使用的是WebClient上传
        }
        /// <summary>
        /// 上传视频素材
        /// </summary>
        /// <param name="url">上传地址</param>
        /// <param name="materialDto">素材文件信息</param>
        /// <param name="contentType">内容类型</param>
        /// <returns></returns>
        private HttpResponseDto UploadVideoMaterial(string url, MaterialDto materialDto, string contentType)
        {
            // 要上传的文件 
            FileStream fileStream = new FileStream(materialDto.FullFileName, FileMode.Open, FileAccess.Read);
            BinaryReader binaryReader = new BinaryReader(fileStream);
            //时间戳 
            string strBoundary = "----------" + DateTime.Now.Ticks.ToString("x");
            byte[] boundaryBytes = Encoding.ASCII.GetBytes("\r\n--" + strBoundary+"--\r\n");

            //请求头部信息 
            StringBuilder stringBuilder = new StringBuilder();
            stringBuilder.Append("--");
            stringBuilder.Append(strBoundary);
            stringBuilder.Append("\r\n");
            stringBuilder.Append($"Content-Disposition: form-data; name=\"media\"; filename=\"{materialDto.FileName+materialDto.FileSuffix}\"");
            stringBuilder.Append("\r\n");
            stringBuilder.Append("Content-Type: ");
            stringBuilder.Append(contentType);
            stringBuilder.Append("\r\n");
            stringBuilder.Append("\r\n");
            string strPostHeader = stringBuilder.ToString();
            byte[] postHeaderBytes = Encoding.UTF8.GetBytes(strPostHeader);

            //请求头部信息 
            stringBuilder = new StringBuilder();
            stringBuilder.Append("\r\n--");
            stringBuilder.Append(strBoundary);
            stringBuilder.Append("\r\n");
            stringBuilder.Append("Content-Disposition: form-data; name=\"description\"");
            stringBuilder.Append("\r\n");
            stringBuilder.Append("\r\n");
            stringBuilder.Append("{\"title\":\"" + materialDto.VideoTitle + "\", \"introduction\":\"" + materialDto.VideoDescription + "\"}");
            string strPostDescriptionHeader = stringBuilder.ToString();
            byte[] postDescriptionHeaderBytes = Encoding.UTF8.GetBytes(strPostDescriptionHeader);

            long length = fileStream.Length + postHeaderBytes.Length + postDescriptionHeaderBytes.Length + +boundaryBytes.Length;

            HttpWebRequest httpWebRequest = (HttpWebRequest)WebRequest.Create(url);
            httpWebRequest.Method = HttpMethod.Post.ToString();
            httpWebRequest.ContentType = "multipart/form-data; boundary=" + strBoundary;
            // 对发送的数据不使用缓存
            httpWebRequest.AllowWriteStreamBuffering = false;
            httpWebRequest.ContentLength = length;
            //设置获得响应的超时时间（300秒） 
            httpWebRequest.Timeout = 300000;


            try
            {
                //每次上传4k 
                int bufferLength = 4096;
                byte[] buffer = new byte[bufferLength];
                
                //开始上传时间 
                DateTime startTime = DateTime.Now;
                int size = binaryReader.Read(buffer, 0, bufferLength);
                Stream postStream = httpWebRequest.GetRequestStream();

                //发送请求头部消息 
                postStream.Write(postHeaderBytes, 0, postHeaderBytes.Length);
                while (size > 0)
                {
                    postStream.Write(buffer, 0, size);
                    size = binaryReader.Read(buffer, 0, bufferLength);
                }
                postStream.Write(postDescriptionHeaderBytes, 0, postDescriptionHeaderBytes.Length);
                //添加尾部的时间戳 
                postStream.Write(boundaryBytes, 0, boundaryBytes.Length);


                postStream.Close();

                //获取服务器端的响应 
                WebResponse webRespon = httpWebRequest.GetResponse();
                Stream responseStream = webRespon.GetResponseStream();
                StreamReader streamReader = new StreamReader(responseStream);

                //读取服务器端返回的消息 
                String responseMessage = streamReader.ReadLine();
                responseStream.Close();
                streamReader.Close();
                HttpResponseDto httpResponseDto = new HttpResponseDto();
                httpResponseDto.ResponseMessage = responseMessage;
                if (httpResponseDto.ResponseMessage.Contains("errcode"))
                {
                    WeChatErrorMessageDto errorMessageDto = JsonConvert.DeserializeObject<WeChatErrorMessageDto>(httpResponseDto.ResponseMessage);
                    if (errorMessageDto.ErrCode == 0)
                    {
                        httpResponseDto.IsError = false;
                    }
                    else
                    {
                        httpResponseDto.IsError = true;
                        httpResponseDto.ResponseMessage = this.httpHandle.GetErrMessage(errorMessageDto);
                    }
                }
                else
                {
                    httpResponseDto.IsError = false;
                }
                return httpResponseDto;
            }
            catch (Exception ex)
            {
                throw new WeChatException("上传失败:" + ex.Message);
            }
            finally
            {
                fileStream.Close();
                binaryReader.Close();
            }
        }
        #endregion

        #region 获取素材
        /// <summary>
        /// 获取素材
        /// </summary>
        /// <param name="mediaId">素材Id</param>
        /// <param name="permanentMaterial">是否永久素材</param>
        /// <param name="isHD">是否高清</param>
        /// <returns>返回数据参考微信文档</returns>
        public HttpResponseDto GetMaterial(string mediaId, bool permanentMaterial, bool isHD)
        {
            string url = string.Empty;
            HttpRequestDto httpRequestDto = new HttpRequestDto();
            httpRequestDto.Headers.Add("Content-Type", "application/json");
            httpRequestDto.Method = MethodType.GET;
            httpRequestDto.ContentType = FormatType.JSON;
            if (permanentMaterial)
            {
                url = string.Format("https://api.weixin.qq.com/cgi-bin/material/get_material?access_token={0}", new BaseSupportService().CreateAccessToken().AccessToken);
                httpRequestDto.Method = MethodType.POST;
                httpRequestDto.Content = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(new { MediaId = mediaId }, WeChatGeneral.JsonSerializerSettings));
                httpRequestDto.Url = url;
                return httpHandle.WebSendRequest(httpRequestDto);
            }
            else
            {
                if (!isHD)
                {
                    url = string.Format("https://api.weixin.qq.com/cgi-bin/media/get?access_token={0}&media_id={1}", new BaseSupportService().CreateAccessToken().AccessToken, mediaId);
                }
                else
                {
                    url = string.Format("https://api.weixin.qq.com/cgi-bin/media/get/jssdk?access_token={0}&media_id={1}", new BaseSupportService().CreateAccessToken().AccessToken, mediaId);
                    httpRequestDto.ContentType = FormatType.Speex;
                }
            }
            httpRequestDto.Url = url;
            return httpHandle.WebSendRequest(httpRequestDto);
        }
        #endregion

        #region 获取素材总数
        /// <summary>
        /// 获取素材总数
        /// </summary>
        /// <returns>{"voice_count":COUNT,"video_count":COUNT,"image_count":COUNT,"news_count":COUNT}</returns>
        public HttpResponseDto GetPermanentMaterialCount()
        {
            string url = string.Format("https://api.weixin.qq.com/cgi-bin/material/get_materialcount?access_token={0}", new BaseSupportService().CreateAccessToken().AccessToken);
            HttpRequestDto httpRequestDto = new HttpRequestDto();
            httpRequestDto.Headers.Add("Content-Type", "application/json");
            httpRequestDto.Method = MethodType.GET;
            httpRequestDto.ContentType = FormatType.JSON;
            httpRequestDto.Url = url;
            return httpHandle.WebSendRequest(httpRequestDto);
        }
        #endregion

        #region 获取永久素材列表
        /// <summary>
        /// 获取永久素材列表
        /// </summary>
        /// <param name="materialType">素材类型</param>
        /// <param name="offset">从全部素材的该偏移位置开始返回，0表示从第一个素材 返回</param>
        /// <param name="count">返回素材的数量，取值在1到20之间</param>
        /// <returns>返回数据参考微信文档</returns>
        public HttpResponseDto GetPermanentMaterialList(MaterialType materialType, int offset, int count)
        {
            string url = string.Format("https://api.weixin.qq.com/cgi-bin/material/batchget_material?access_token={0}", new BaseSupportService().CreateAccessToken().AccessToken);
            HttpRequestDto httpRequestDto = new HttpRequestDto();
            httpRequestDto.Headers.Add("Content-Type", "application/json");
            httpRequestDto.Method = MethodType.POST;
            httpRequestDto.ContentType = FormatType.JSON;
            httpRequestDto.Content = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(new { Type = materialType.ToString().ToLower(), Offset = offset, Count = count }, WeChatGeneral.JsonSerializerSettings));
            httpRequestDto.Url = url;
            return httpHandle.WebSendRequest(httpRequestDto);
        }
        #endregion

        #region 删除永久素材
        /// <summary>
        /// 删除永久素材
        /// </summary>
        /// <param name="mediaId">永久素材Id</param>
        /// <returns>{"errcode":ERRCODE,"errmsg":ERRMSG} 正常情况下调用成功时，errcode将为0。</returns>
        public HttpResponseDto DeletePermanentMaterial(string mediaId)
        {
            string url = string.Format("https://api.weixin.qq.com/cgi-bin/material/del_material?access_token={0}", new BaseSupportService().CreateAccessToken().AccessToken);
            HttpRequestDto httpRequestDto = new HttpRequestDto();
            httpRequestDto.Headers.Add("Content-Type", "application/json");
            httpRequestDto.Method = MethodType.POST;
            httpRequestDto.ContentType = FormatType.JSON;
            httpRequestDto.Content = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(new { MediaId = mediaId }, WeChatGeneral.JsonSerializerSettings));
            httpRequestDto.Url = url;
            return httpHandle.WebSendRequest(httpRequestDto);
        }
        #endregion
    }
}
